<script setup lang="ts" name="FolderTree">
import { ElTree } from 'element-plus'
import { FolderApi, FolderType } from '@/api/infra/resource/library'
import { defaultProps, handleTree, treeLevel, treeToAscending, findNodeById, getTreeNodeChildren, getIdFromArray, getTreeNodeParents } from '@/utils/tree'
import FolderForm from './FolderForm.vue'
import { checkPermit } from '@/utils/permit'
import { cloneDeep } from 'lodash-es'

const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const emits = defineEmits(['node-click', 'node-remove', 'breadcrumb', 'init'])

const queryLoading = ref(true) // 列表的加载中
const folderName = ref('')
const folderList = ref<Tree[]>([]) // 树形结构
const treeRef = ref<InstanceType<typeof ElTree>>()
const checkedKey = ref(0) // 已选中的节点 id
const collapseKeys = ref<number[]>([]) // 已折叠的节点 id 数组
const rightClickData = ref() // 右键点击的节点数据
const breadcrumbList = ref<FolderType[]>([])

// 获取树形结构面包屑
const getBreadcrumbList = (data, node) => {
  const result = getTreeNodeParents(data, node)
  const nodeData = cloneDeep(node)
  if (nodeData.children && nodeData.children.length > 0) {
    nodeData.children = []
  }
  result.push(nodeData)
  return result
}

/** 获得文件夹树 */
const getTree = async () => {
  queryLoading.value = true
  try {
    const res = await FolderApi.list()
    folderList.value = [{
      id: 0,
      parentId: undefined,
      name: t('infra.resource.library', 'title'),
      children: treeToAscending(handleTree(res))
    }]
    treeLevel(folderList.value, false, [], 0)
    breadcrumbList.value = getBreadcrumbList(folderList.value, findNodeById(folderList.value, checkedKey.value))
    emits('breadcrumb', breadcrumbList.value)
    nextTick(() => {
      treeRef.value!.setChecked(checkedKey.value, true, false)
    })
  } finally {
    queryLoading.value = false
  }
}

const init = async () => {
  await getTree()
  emits('init', folderList.value)
}

/** 基于名字过滤 */
const filterNode = (name: string, data: Tree) => {
  if (!name) return true
  if (!data.name) return false
  return data.name.includes(name)
}

/** 处理文件夹被点击 */
const handleNodeClick = async (row: { [key: string]: any }) => {
  treeRef.value!.setChecked(checkedKey.value, false, false)
  checkedKey.value = row.id
  treeRef.value!.setChecked(checkedKey.value, true, false)
  breadcrumbList.value = getBreadcrumbList(folderList.value, row)
  emits('breadcrumb', breadcrumbList.value)
  emits('node-click', row)
}

/** 重置已选中节点 */
const resetFolder = () => {
  treeRef.value!.setChecked(checkedKey.value, false, false)
  checkedKey.value = 0
  treeRef.value!.setChecked(checkedKey.value, true, false)
  breadcrumbList.value = getBreadcrumbList(folderList.value, findNodeById(folderList.value, checkedKey.value))
  emits('breadcrumb', breadcrumbList.value)
}

/** 更新已选中节点 */
const updateFolder = (row) => {
  treeRef.value!.setChecked(checkedKey.value, false, false)
  checkedKey.value = row.id
  treeRef.value!.setChecked(checkedKey.value, true, false)
  breadcrumbList.value = getBreadcrumbList(folderList.value, row)
  emits('breadcrumb', breadcrumbList.value)
}

defineExpose({ init, getTree, resetFolder, updateFolder }) // 暴漏方法
/** 处理文件夹节点被展开 */
const handleNodeExpand = async row => {
  const index = collapseKeys.value.findIndex(item => item === row.id)
  if (index !== -1) {
    collapseKeys.value.splice(index, 1)
  }
}

/** 处理文件夹节点被折叠 */
const handleNodeCollapse = async row => {
  collapseKeys.value.push(row.id)
}

/** 处理树形结构折叠时的线条显示隐藏状态 */
const isCollapse = id => {
  return collapseKeys.value.some(item => item === id)
}

// 右键点击节点事件
const handleNodeContextmenu = (_e, data) => {
  if (checkPermit(['infra:resource-library:folder:create', 'infra:resource-library:folder:delete', 'infra:resource-library:folder:rename', 'infra:resource-library:folder:move'])) {
    rightClickData.value = data
  }
}

/** 根节点不允许被拖拽 */
const handleAllowDrag = node => {
  if (node.data.id === 0) {
    return false
  } else {
    return true
  }
}

/** 根节点不允许被放置 */
const handleAllowDrop = (_draggingNode, dropNode, type) => {
  if (dropNode.data.id === 0 && (type === 'prev' || type === 'next')) {
    return false
  } else {
    return true
  }
}

/** 节点拖拽放置后触发事件 */
const handleNodeDrop = async (draggingNode, dropNode, type) => {
  console.log(dropNode, type)
  let formData = {
    id: draggingNode.data.id,
    parentId: draggingNode.data.parentId,
    name: draggingNode.data.name,
    sort: draggingNode.data.sort
  }
  if (type === 'before') {
    formData.parentId = dropNode.data.parentId
    formData.sort = dropNode.data.sort - 1
  } else if (type === 'after') {
    formData.parentId = dropNode.data.parentId
    formData.sort = dropNode.data.sort + 1
  } else {
    formData.parentId = dropNode.data.id
  }
  await FolderApi.move(formData)
  message.success(t('message', 'updateSuccess'))
  await getTree()
}

/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, data?: FolderType) => {
  formRef.value.open(type, data)
}

/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    const ids = getIdFromArray(getTreeNodeChildren(folderList.value, { id }))
    // 发起删除
    await FolderApi.delete(id)
    message.success(t('message.del', 'success'))
    let isRefresh = false
    const isRemove = ids.some(v => v === checkedKey.value)
    if (isRemove) {
      checkedKey.value = 0
      isRefresh = true
    }
    // 刷新列表
    await getTree()
    nextTick(() => {
      emits('node-remove', { isRefresh, id: checkedKey.value, ids, folderList: folderList.value })
    })
  } catch { }
}

// 树结构过滤
const filterTreeList = () => {
  treeRef.value!.filter(folderName.value)
}

/** 监听 folderName */
watch(
  () => folderName.value,
  (val) => {
    treeRef.value!.filter(val)
  }
)

/** 初始化 */
onMounted(async () => {
  await init()
})
</script>

<template>
  <el-aside width="240px" class="flex flex-col">
    <el-card shadow="never" class="flex flex-1 flex-col">
      <template #header>
        <div class="card-header">
          <el-input v-model="folderName" :placeholder="t('infra.resource.library.folder.rules', 'name')" clearable @input="filterTreeList" />
        </div>
      </template>
      <div class="folder-list flex-1">
        <el-scrollbar>
          <el-tree
            ref="treeRef"
            class="tree-level"
            :data="folderList"
            :props="defaultProps"
            node-key="id"
            default-expand-all
            :expand-on-click-node="false"
            :filter-node-method="filterNode"
            :highlight-current="false"
            :check-on-click-node="true"
            check-strictly
            :draggable="checkPermit(['infra:resource-library:folder:move'])"
            :allow-drag="handleAllowDrag"
            :allow-drop="handleAllowDrop"
            @node-drop="handleNodeDrop"
            @node-click="handleNodeClick"
            @node-expand="handleNodeExpand"
            @node-contextmenu="handleNodeContextmenu"
            @node-collapse="handleNodeCollapse">
            <template #default="{ node, data }">
              <span v-contextmenu:contextmenu class="item-tree-node">
                <span>{{ node.label }}</span>
                <template v-for="item in data.level">
                  <span v-if="!data.hideLine.includes(item)" :key="item" :class="['item-tree-node-line-vertical', data.isLast && item == data.level ? 'is-last' : '', data.isFirst && data.level === 1 ? 'is-first' : '']" :style="{ left: 18 * (item - 1) + 12 + 'px' }" />
                </template>
                <span v-if="!data.isLeaf && !isCollapse(data.id)" class="item-tree-node-line-vertical is-first" :style="{ left: 18 * (data.level) + 12 + 'px' }" />
                <span class="item-tree-node-line-horizontal" :style="{ left: 18 * (data.level - 1) + 12 + 'px' }" />
              </span>
            </template>
          </el-tree>
        </el-scrollbar>
        <div class="btn-pane">
          <el-button icon="Plus" plain type="success" @click="openForm('create')">{{ t('action', 'create') }}</el-button>
        </div>
      </div>
      <!-- 右键弹窗 -->
      <v-contextmenu ref="contextmenu" :disabled="(!checkPermit(['infra:resource-library:folder:create', 'infra:resource-library:folder:delete', 'infra:resource-library:folder:rename', 'infra:resource-library:folder:move']))">
        <v-contextmenu-item v-if="checkPermit(['infra:resource-library:folder:create'])" @click="openForm('create', rightClickData)">{{ t('action', 'create') }}</v-contextmenu-item>
        <v-contextmenu-item v-if="checkPermit(['infra:resource-library:folder:delete'])" :disabled="rightClickData && rightClickData.id === 0" @click="handleDelete(rightClickData.id)">{{ t('action', 'delete') }}</v-contextmenu-item>
        <v-contextmenu-item v-if="checkPermit(['infra:resource-library:folder:rename'])" :disabled="rightClickData && rightClickData.id === 0" @click="openForm('rename', rightClickData)">{{ t('action', 'rename') }}</v-contextmenu-item>
        <v-contextmenu-item v-if="checkPermit(['infra:resource-library:folder:move'])" :disabled="rightClickData && rightClickData.id === 0" @click="openForm('move', rightClickData)">{{ t('action', 'move') }}</v-contextmenu-item>
      </v-contextmenu>
      <!-- 表单弹窗：添加/修改/重命名/移动到 -->
      <FolderForm ref="formRef" :folder-list="folderList" @success="getTree" />
    </el-card>
  </el-aside>
</template>

<style scoped lang="scss">
.el-aside{
  margin-right: var(--el-space);
  overflow: inherit;

  .el-card {
    :deep(.el-card__header) {
      padding: calc(var(--el-space) - 5px);
    }

    :deep(.el-card__body) {
      position: relative;
      display: flex;
      flex: 1;
      padding: 0;

      .el-scrollbar {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;

        &.is-pick{
          .el-scrollbar__wrap {
            padding: calc(var(--el-space) / 2);
          }
        }

        .el-scrollbar__wrap {
          padding: calc(var(--el-space) / 2) calc(var(--el-space) / 2) 54px;
        }
      }
    }
  }
}

.folder-list {
  position: relative;

  .btn-pane{
    position: absolute;
    bottom: 0;
    left: 0;
    z-index: 2;
    display: flex;
    width: 100%;
    padding: 12px;
    pointer-events: none;

    .el-button{
      flex: 1;
      pointer-events: all;
    }
  }
}

.tree-level {
  :deep(.el-tree-node){
    &.is-checked{
      > .el-tree-node__content {
        background-color: var(--el-color-primary-light-9)!important;
      }
    }
  }
}
</style>
